package com.kming.crypto.config;

import com.kming.crypto.AlgorithmNotFoundException;
import com.kming.crypto.algorithm.Algorithm;
import com.kming.crypto.util.ReflectionUtils;
import lombok.SneakyThrows;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 2022/5/13 8:54
 */
@ConfigurationProperties(prefix = "controller.crypto")
public class CryptoConfig {
    private static Algorithm defaultEncrypt;
    private static Algorithm defaultDecrypt;
    public static final Map<Class<? extends Algorithm>,Algorithm> algorithmMap = new ConcurrentHashMap<>(0);

    /**
     * 获取加密算法
     * @param clazz 算法类
     * @return 算法
     */
    public static Algorithm getEncrypt(Class<? extends Algorithm> clazz){
        if (clazz == Algorithm.class){
            if (defaultEncrypt == null){
                throw new AlgorithmNotFoundException("默认加密算法未配置！");
            }
            return defaultEncrypt;
        }
        return getAlgorithm(clazz);
    }

    /**
     * 获取解密算法
     * @param clazz 算法类
     * @return 算法
     */
    public static Algorithm getDecrypt(Class<? extends Algorithm> clazz){
        if (clazz == Algorithm.class){
            if (defaultDecrypt == null){
                throw new AlgorithmNotFoundException("默认解密算法未配置！");
            }
            return defaultDecrypt;
        }
        return getAlgorithm(clazz);
    }

    public static Algorithm getAlgorithm(Class<? extends Algorithm> clazz) {
        if (!algorithmMap.containsKey(clazz)){
            throw new AlgorithmNotFoundException(
                    String.format("未找到类型为%s的算法类，请检查配置文件中是否有配置", clazz.getName()));
        }
        return algorithmMap.get(clazz);
    }

    /**
     * 根据配置文件自动添加算法，可添加多个算法，并在注解中指定使用的算法
     * @see [application.yml]controller.crypto.algorithms
     * class-name: 算法类名
     * 其余属性为构造器参数
     * @param algorithms 配置文件解析的算法类名，构造器参数
     */
    public void setAlgorithms(Set<Map<String, Object>> algorithms) {
        for (Map<String, Object> algorithm: algorithms) {
            Algorithm algorithm1 = getAlgorithm(algorithm);
            algorithmMap.put(algorithm1.getClass(), algorithm1);
        }
    }

    /**
     * 根据配置文件设置默认的加密算法，如果注解未指定加密算法，则默认使用该加密算法
     * @see [application.yml]controller.crypto.defaultEncrypt
     * @param defaultEncrypt 配置文件解析的加密算法类名，构造器参数
     */
    public void setDefaultEncrypt(Map<String, Object> defaultEncrypt) {
        CryptoConfig.defaultEncrypt = getAlgorithm(defaultEncrypt);
    }

    /**
     * 根据配置文件设置默认的解密算法，如果注解未指定解密算法，则默认使用该解密算法
     * @see [application.yml]controller.crypto.defaultDecrypt
     * @param defaultDecrypt 配置文件解析的解密算法类名，构造器参数
     */
    public  void setDefaultDecrypt(Map<String, Object> defaultDecrypt) {
        CryptoConfig.defaultDecrypt = getAlgorithm(defaultDecrypt);
    }


    @SneakyThrows
    private static Algorithm getAlgorithm(Map<String, Object> algorithm){
        String className = algorithm.get("class-name").toString();
        String[] paramNames = algorithm.keySet().stream().filter(key->!"class-name".equals(key)).toArray(String[]::new);
        Constructor<?> ctor = ReflectionUtils.findConstructor(className, paramNames);
        return (Algorithm) ctor.newInstance(Arrays.stream(paramNames).map(algorithm::get).toArray());
    }
}
